# -*- coding: utf-8 -*-

import jumble.group
import jumble.texture
import jumble.label
import jumble.layout
import jumble.classstylemanager

import clutter
import utils.actions
import logging

from os.path import splitext, split, join, dirname

import gobject

# Feedback Part
from twisted.internet import threads, reactor, defer
from twisted.web.client import getPage
from threading import Thread
from twisted.web.error import Error
import urllib
import simplejson as json


log = logging.getLogger("AlicanteClutter")
ROOT_DIR = dirname(__file__)

# To do, use a constants file containing all the constants
FEEDBACKSERVER_IP = "10.193.250.104"
FEEDBACKSERVER_PORT = 8080



class EventBase(jumble.group.Group):
    def __init__(self, eventsOverlay, eventID, path):
        log.debug("Load event")
        jumble.group.Group.__init__ (self)
        self.eventsOverlay = eventsOverlay
        self.enabled = True
        self.wasOnFocus = None # Save the focus when switch to another event
        self.isShown = False
        self.name = None
        self.style = None
        self.description = None
        self.eventID = eventID
        self.path = join(ROOT_DIR, path)
        self.available = False
        
    def getName(self):
        log.debug("Get name: %s" % self.name)
        return self.name
        
    def isShown(self):
        return self.isShown

    def loadStyles(self):
        if self.style:  
            log.debug("Load Style")
            clutter.threads_enter()
            try:
                #print self.style.replace('$$_PATH_$$', self.path)
                #pass
                jumble.classstylemanager.registry.add_json_style( self.style.replace('$$_PATH_$$', self.path) )
            except:
                log.error('Impossible to load style in event %s' % self.name)
            clutter.threads_leave()

    def load(self):
        """
        Load the event elements
        """
        raise NotImplementedError()

    def run(self):
        """
        If there is some animations when the event is displayed, they should be here
        """
        log.debug("Event %s is running" % self.name)
        
    def stop(self):
        """
        If there is some animations when the event is removed, they should be here
        """
        log.debug("Event %s stop running" % self.name)
        self.isShown = False        
        
    def hide(self):
        log.debug("Hide event %s" % self.name)
        self.saveFocus()
        self.set_opacity(0)
        self.stop()
        self.disable()    
        self.eventsOverlay.layout.remove_actor(self)
        
        
    def saveFocus(self):
        self.wasOnFocus = self.get_focused_child()
        
    def show(self):
        log.debug("Showing event: %s" % self.name)

        #try:
        if not self.eventsOverlay.layout.contains(self):
            self.eventsOverlay.layout.add_actor(self)
            self.eventsOverlay.layout.set_align(self, jumble.layout.Layout.ALIGN_S)
            
        self.enable()
        if self.layout == None:
            self.load()
            if self.get_focused_child() == None:
                self.set_focus_on_first_child()
                
        elif self.wasOnFocus != None:
            self.wasOnFocus.set_focus(True)
        else:
            self.set_focus_on_first_child()
            
            
        self.set_opacity(255)   
        self.show_all()
        self.run()
        self.isShown = True
        #except:
            #log.error("Unable to show event %s" % self.name)


    def sendToServer(self, data):
        log.debug("Sending data to server")
        
        result = {}
        result['id'] = self.eventID
        result['user'] = "Florian"
        result['content'] = data
        
        #strresult = json.dumps(result)
        strresult = urllib.urlencode(result)

        #print "http://%s:%d/Feedback/feedback?%s" % (FEEDBACKSERVER_IP, FEEDBACKSERVER_PORT, strresult)
        
        def sendData():
            try:
                #result = threads.blockingCallFromThread(
                    #reactor, getPage, "http://%s:%d/Feedback/feedback?%s" % (FEEDBACKSERVER_IP, FEEDBACKSERVER_PORT, strresult), timeout = 10)
                result = threads.blockingCallFromThread(
                    reactor, getPage, "http://%s:%d/Feedback/feedback" % (FEEDBACKSERVER_IP, FEEDBACKSERVER_PORT), headers={'Content-Type':'application/x-www-form-urlencoded '}, method="POST", postdata =  strresult, timeout = 10)
                #result = threads.blockingCallFromThread(
                    #reactor, getPage, "http://%s:%d/Feedback/feedback" % (FEEDBACKSERVER_IP, FEEDBACKSERVER_PORT), headers={"Content-Type":"application/json"}, method="POST", postdata =  strresult, timeout = 10)
            except Error, exc:
                print exc
            else:
                print result
                
        reactor.callInThread(sendData)

    def loadFeedback(self, feedbackData = None):
        """"
        When the event receives a feedback, this function will be call with the new data.xml file
        """
        pass

    def on_key_press_event(self, event):
        pass


class EventsManager:
    def __init__(self, stage, mapping):
        log.debug("Init Events Manager")
        
        self.item_events = {}
        self.current = None
        
        self.stage = stage
        
        div = jumble.group.Group(enabled=True)
        div.set_size(*self.stage.get_size())
        div.set_layout(jumble.layout.HLayout(name="EventLayout"))
        
        self.eventsOverlay = jumble.group.Group(enabled=True)
        self.eventsOverlay.set_layout( jumble.layout.VLayout(name="southLayout", vpad=100) )
        self.eventsOverlay.set_size(*self.stage.get_size())    
        div.layout.add_actor(self.eventsOverlay)
        div.layout.set_align(self.eventsOverlay, jumble.layout.Layout.ALIGN_S)
        
        #self.eventsOverlay.layout.set_size(*self.eventsOverlay.get_size())
        self.stage.add(div)
        
        self.availableEvents = jumble.group.Group(enabled=True)
        self.availableEvents.set_layout(jumble.layout.HLayout(name="EventLayout"))
        self.availableEvents.set_size(*self.stage.get_size())        
        
        self.layoutAvailableEvents = jumble.layout.VLayout(vpad=20, hpad=40)    
        #self.message = jumble.label.Label(name="events_intro", text="")
        #self.layoutAvailableEvents.add_actor(self.message, expand=True)
        self.availableEvents.layout.add_layout(self.layoutAvailableEvents, align=jumble.layout.Layout.ALIGN_NW, expand=10)
        self.stage.add(self.availableEvents)
        
        self.stage.show_all()

        # Link actions with keys
        self.actionsMapping = mapping        
        self.listActions = {
            utils.actions.SHOW_WIDGETS : self.showEvents,            
            utils.actions.HIDE_WIDGETS : self.hideEvents,
            utils.actions.FADE_IN_WIDGETS : self.fadeInEvents,
            utils.actions.FADE_OUT_WIDGETS : self.fadeOutEvents,
            utils.actions.REMOVE_WIDGET : self.removeEvent,
            utils.actions.SHOW_AVAILABLE_WIDGETS : self.update_available_events
            }
        self.stage.connect('key-press-event', self.on_key_press_event)


    def eventExist(self, filename, hash_zip):
        return False

    def addEvent(self):
        log.debug("Add event")
        
    def load(self, eventID, req_path):
        """
        Load the event in req_path as a python module if not already done
        """
        log.debug("Load() called for '%s'" % req_path)

        if eventID in self.item_events:
            if self.item_events[eventID].path != join(ROOT_DIR, split(req_path)[0]):
                log.debug("New version of the event, load feedback")
                from xml.dom import minidom
                try:
                    fxml = open(join(ROOT_DIR, split(req_path)[0], "data.xml"))
                    xmldom = minidom.parse(fxml)
                    fxml.close()
                except:
                    log.error("Unable to parse data.xml")
                else:
                    try:
                        feedback = xmldom.firstChild
                        resultObject = []

                        for result in feedback.childNodes:
                            try:
                                resultObject.append({
                                    'user': result.getAttribute("user"),
                                    'content': result.getAttribute("content")
                                    })
                            except:
                                log.error("Wrong node")
                    except:
                        log.error("Can't load result from data.xml")
                    else:
                        try:
                            self.item_events[eventID].loadFeedback(resultObject)
                        except:
                            log.error("Can't display feedback data")
                      
        else:  # no, try import
            module_path = '.'.join(filter(None, splitext(req_path)[0].split('/')))
            m = __import__(module_path, globals(), locals(), ['Event'])
            
            self.item_events[eventID] =  m.Event( self.eventsOverlay, eventID, split(req_path)[0] )
            self.item_events[eventID].loadStyles() # Move that in load?
            

    def addExampleEvent(self, path):
        log.debug("Add example event")
        self.load(path)
        self.startEvent(path)
        self.update_available_events()

        
    def removeEvent(self, name = None):
        """
        This function is to remove the event from the memory, should be call if the event will not longer be displayed
        """
        log.debug("Remove event")
        if name == None:
            name = self.current
            self.current = None
        if self.item_events.has_key(name):
            self.item_events[name].hide()
            del(self.item_events[name])

    def eventListPress(self, group, button, eventID):
        self.showEvent(eventID)

    def showEvent(self, eventID):
        log.debug("Show the event: %s" % eventID)        
        if eventID != self.current:
            if self.item_events.has_key(self.current):
                self.item_events[self.current].hide()
            self.current = eventID
            
        self.item_events[self.current].show()
        self.availableEvents.set_opacity(0)


    def startEvent(self, eventID):
        log.debug("Add to available events: %s" % eventID)
        if self.item_events.has_key(eventID):
            self.item_events[eventID].available = True
        return False

    def stopEvent(self, eventID = None):
        log.debug("Remove from available events: %s" % eventID)
        
        if self.current == eventID:
            self.current = None
        elif eventID == None:
            eventID = self.current
            self.current = None
            
        if self.item_events.has_key(eventID):
            self.item_events[eventID].hide()
            self.item_events[eventID].available = False
        return False

    def update_available_events(self):
        """
        Scan the events to find the available events
        """
        log.debug("Show available events")
        self.layoutAvailableEvents.clear()
        
        if self.current:
            self.item_events[self.current].saveFocus()
            
        self.message = jumble.label.Label(name="events_intro")
        self.layoutAvailableEvents.add_actor(self.message, expand=True)

        empty = True
        
        for eventID, eventObject in self.item_events.iteritems():
            if eventObject.available:
                empty = False
                log.debug("Event %s available" % eventID)
                #self.layoutAvailableEvents.add_actor(jumble.label.Label(name=("Label%d" % i), text=eventObject.getName()), expand=True)
                event_button = jumble.button.Button(name=("listevents"), text=eventObject.getName(), add_background=False)
                self.layoutAvailableEvents.add_actor(event_button)
                event_button.connect('clicked', self.eventListPress, eventID)
       
        if empty:
            available_msg = "No available widget"
        else:
            available_msg = "Events available:"
        self.message.set_text(available_msg)
       
        self.availableEvents.set_focus_on_first_child()
        self.availableEvents.set_opacity(255)
        return False
        
    #def moveToNextEvent(self): # Not
        #log.debug("Move to next event")
        #if len(self.item_events):
           #if self.current and ( (self.current + 1) > len(self.item_events) ):
                #self.showEvent(self.current+1)
           #else:
               #self.showEvent(0)

    def fadeOutEvents(self):
        if self.eventsOverlay.get_opacity() == 255:
            timeline = clutter.Timeline(200)
            alpha = clutter.Alpha(timeline, clutter.EASE_OUT_SINE)
            self.behaviour_opacity = clutter.BehaviourOpacity(opacity_start=255, opacity_end=0, alpha=alpha)
            self.behaviour_opacity.apply(self.eventsOverlay)
            timeline.start()

        
    def fadeInEvents(self):
        if self.eventsOverlay.get_opacity() == 0:
            timeline = clutter.Timeline(500)
            alpha = clutter.Alpha(timeline, clutter.EASE_OUT_SINE)
            self.behaviour_opacity = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=alpha)
            self.behaviour_opacity.apply(self.eventsOverlay)
            timeline.start()
        
    def hideEvents(self):
        self.eventsOverlay.set_opacity(0)
        
    def showEvents(self):
        self.eventsOverlay.set_opacity(255)      
        
    def on_key_press_event(self, stage, event):
        # Shoud do the key or remote control mapping here
        #log.debug("Key press")

        if self.listActions.has_key(self.actionsMapping.action(event)) and callable(self.listActions[self.actionsMapping.action(event)]):
            self.listActions[self.actionsMapping.action(event)]()
        
        for name, eventObject in self.item_events.iteritems():
            if eventObject.isShown:
                eventObject.on_key_press_event(event)
    

